package com.lee.common.json;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.MissingNode;
import lombok.AllArgsConstructor;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import java.util.function.Supplier;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * 对异常做处理的ObjectMapper
 *
 * @author Lee HN
 * @date 2020/9/22 11:19
 */
@Component
@AllArgsConstructor
public final class SafeJsonMapper {

    private final ObjectMapper objectMapper;

    private static final String DefObjJson = "{}";

    private static final String DefArrayJson = "[]";

    private static final byte[] DefObjJsonBytes = DefObjJson.getBytes(UTF_8);

    private static final byte[] DefArrayJsonBytes = DefArrayJson.getBytes(UTF_8);

    public <T> T readValue(String content, Class<T> valueType, Supplier<T> standby) {
        T t;
        try {
            t = objectMapper.readValue(content, valueType);
        } catch (Exception e) {
            t = standby.get();
        }
        return t;
    }

    @Nullable
    public <T> T readValue(String content, Class<T> valueType) {
        T t = null;
        try {
            t = objectMapper.readValue(content, valueType);
        } catch (Exception ignored) { }
        return t;
    }

    public <T> T readValue(byte[] src, Class<T> valueType, Supplier<T> standby) {
        T t;
        try {
            t = objectMapper.readValue(src, valueType);
        } catch (Exception e) {
            t = standby.get();
        }
        return t;
    }

    @Nullable
    public <T> T readValue(byte[] src, Class<T> valueType) {
        T t = null;
        try {
            t = objectMapper.readValue(src, valueType);
        } catch (Exception ignored) { }
        return t;
    }

    public <T> T readValue(String content, TypeReference<T> valueTypeRef, Supplier<T> standby) {
        T t;
        try {
            t = objectMapper.readValue(content, valueTypeRef);
        } catch (Exception e) {
            t = standby.get();
        }
        return t;
    }

    @Nullable
    public <T> T readValue(String content, TypeReference<T> valueTypeRef) {
        T t = null;
        try {
            t = objectMapper.readValue(content, valueTypeRef);
        } catch (Exception ignored) { }
        return t;
    }

    public <T> T readValue(byte[] src, TypeReference<T> valueTypeRef, Supplier<T> standby) {
        T t;
        try {
            t = objectMapper.readValue(src, valueTypeRef);
        } catch (Exception e) {
            t = standby.get();
        }
        return t;
    }

    @Nullable
    public <T> T readValue(byte[] src, TypeReference<T> valueTypeRef) {
        T t = null;
        try {
            t = objectMapper.readValue(src, valueTypeRef);
        } catch (Exception ignored) { }
        return t;
    }

    public JsonNode readTree(String content) {
        JsonNode jsonNode;
        try {
            jsonNode = objectMapper.readTree(content);
        } catch (Exception e) {
            jsonNode = MissingNode.getInstance();
        }
        return jsonNode;
    }

    public JsonNode readTree(byte[] content) {
        JsonNode jsonNode;
        try {
            jsonNode = objectMapper.readTree(content);
        } catch (Exception e) {
            jsonNode = MissingNode.getInstance();
        }
        return jsonNode;
    }

    public String writeAsString(Object value) {
        if (value == null) return null;
        String json;
        try {
            json = objectMapper.writeValueAsString(value);
        } catch (Exception e) {
            json = isArray(value) ? DefArrayJson : DefObjJson;
        }
        return json;
    }

    public byte[] writeAsBytes(Object value) {
        if (value == null) return null;
        byte[] jsonBytes;
        try {
            jsonBytes = objectMapper.writeValueAsBytes(value);
        } catch (Exception e) {
            jsonBytes = isArray(value) ? DefArrayJsonBytes : DefObjJsonBytes;
        }
        return jsonBytes;
    }

    private boolean isArray(Object value) {
        return value.getClass().isArray() || value instanceof Iterable;
    }

}
